/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
 * HS2304, trng module of 1711, Copyright (c) 2023-, Huawei
 *
 *
 *
 * This code is licensed under the GNU GPLv2 and later.
 */

#include "qemu/osdep.h"
#include "hw/qdev-properties.h"
#include "sysemu/runstate.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qemu/qemu-print.h"
#include "crypto/random.h"
#include "qapi/error.h"

#define HS2304_REGION_SIZE 0x1000

#define TYPE_HS2304_TRNG "hs2304"
#define HS2304_TRNG(obj) \
    OBJECT_CHECK(hs2304_state, (obj), TYPE_HS2304_TRNG)

/* 随机数输出寄存器0 */
#define HS2304_TRNG_OUTPUT_0        0x0000
/* 随机数输出寄存器1 */
#define HS2304_TRNG_OUTPUT_1        0x0004
/* 随机数输出寄存器2 */
#define HS2304_TRNG_OUTPUT_2        0x0008
/* 随机数输出寄存器3 */
#define HS2304_TRNG_OUTPUT_3        0x000C
/* 数据输入寄存器0 */
#define HS2304_TRNG_INPUT_0         0x0000
/* 数据输入寄存器1 */
#define HS2304_TRNG_INPUT_1         0x0004
/* 数据输入寄存器2 */
#define HS2304_TRNG_INPUT_2         0x0008
/* 数据输入寄存器3 */
#define HS2304_TRNG_INPUT_3         0x000C
/* 状态寄存器 */
#define HS2304_TRNG_STATUS          0x0010
/* 中断清除寄存器 */
#define HS2304_TRNG_INTACK          0x0010
/* 控制寄存器 */
#define HS2304_TRNG_CONTROL         0x0014
/* 配置寄存器 */
#define HS2304_TRNG_CONFIG          0x0018
/* 关断中断控制寄存器 */
#define HS2304_TRNG_ALARMCNT        0x001C
/* FRO使能寄存器 */
#define HS2304_TRNG_FROENABLE       0x0020
/* FRO调节寄存器 */
#define HS2304_TRNG_FRODETUNE       0x0024
/* 告警事件log寄存器 */
#define HS2304_TRNG_ALARMMASK       0x0028
/* 告警关断寄存器 */
#define HS2304_TRNG_ALARMSTOP       0x002C
/* 原始噪声bit低位寄存器 */
#define HS2304_TRNG_RAW_L           0x0030
/* 原始噪声bit高位寄存器 */
#define HS2304_TRNG_RAW_H           0x0034
/* SP 800-90B测试控制寄存器 */
#define HS2304_TRNG_SPB_TESTS       0x0038
/* 主时间计数器寄存器 */
#define HS2304_TRNG_COUNT           0x003C
/* 加工功能输出寄存器0 */
#define HS2304_TRNG_COND_0          0x0040
/* 加工功能输出寄存器1 */
#define HS2304_TRNG_COND_1          0x0044
/* 加工功能输出寄存器2 */
#define HS2304_TRNG_COND_2          0x0048
/* 加工功能输出寄存器3 */
#define HS2304_TRNG_COND_3          0x004C
/* 加工功能输出寄存器4 */
#define HS2304_TRNG_COND_4          0x0050
/* 加工功能输出寄存器5 */
#define HS2304_TRNG_COND_5          0x0054
/* 加工功能输出寄存器6 */
#define HS2304_TRNG_COND_6          0x0058
/* 加工功能输出寄存器7 */
#define HS2304_TRNG_COND_7          0x005C
/* DRBG AES-256 key寄存器0 */
#define HS2304_TRNG_KEY_0           0x0040
/* DRBG AES-256 key寄存器1 */
#define HS2304_TRNG_KEY_1           0x0044
/* DRBG AES-256 key寄存器2 */
#define HS2304_TRNG_KEY_2           0x0048
/* DRBG AES-256 key寄存器3 */
#define HS2304_TRNG_KEY_3           0x004C
/* DRBG AES-256 key寄存器4 */
#define HS2304_TRNG_KEY_4           0x0050
/* DRBG AES-256 key寄存器5 */
#define HS2304_TRNG_KEY_5           0x0054
/* DRBG AES-256 key寄存器6 */
#define HS2304_TRNG_KEY_6           0x0058
/* DRBG AES-256 key寄存器7 */
#define HS2304_TRNG_KEY_7           0x005C
/* DRBG输入寄存器0 */
#define HS2304_TRNG_PS_AI_0         0x0040
/* DRBG输入寄存器1 */
#define HS2304_TRNG_PS_AI_1         0x0044
/* DRBG输入寄存器2 */
#define HS2304_TRNG_PS_AI_2         0x0048
/* DRBG输入寄存器3 */
#define HS2304_TRNG_PS_AI_3         0x004C
/* DRBG输入寄存器4 */
#define HS2304_TRNG_PS_AI_4         0x0050
/* DRBG输入寄存器5 */
#define HS2304_TRNG_PS_AI_5         0x0054
/* DRBG输入寄存器6 */
#define HS2304_TRNG_PS_AI_6         0x0058
/* DRBG输入寄存器7 */
#define HS2304_TRNG_PS_AI_7         0x005C
/* DRBG输入寄存器8 */
#define HS2304_TRNG_PS_AI_8         0x0060
/* DRBG输入寄存器9 */
#define HS2304_TRNG_PS_AI_9         0x0064
/* DRBG输入寄存器10 */
#define HS2304_TRNG_PS_AI_10        0x0068
/* DRBG输入寄存器11 */
#define HS2304_TRNG_PS_AI_11        0x006C
/* bits和runs 计数器状态寄存器 */
#define HS2304_TRNG_RUN_CNT         0x0040
/* 1bit 0/1 runs计数器寄存器 */
#define HS2304_TRNG_RUN_1           0x0044
/* 2bit 0/1 runs计数器寄存器 */
#define HS2304_TRNG_RUN_2           0x0048
/* 3bit 0/1 runs计数器寄存器 */
#define HS2304_TRNG_RUN_3           0x004C
/* 4bit 0/1 runs计数器寄存器 */
#define HS2304_TRNG_RUN_4           0x0050
/* 5bit 0/1 runs计数器寄存器 */
#define HS2304_TRNG_RUN_5           0x0054
/* 大于等于6bit 0/1 runs计数器寄存器 */
#define HS2304_TRNG_RUN_6_UP        0x0058
/* "Monobit"测试计数器寄存器 */
#define HS2304_TRNG_MONOBITCNT      0x005C
/* "Poker"测试计数器0-3寄存器 */
#define HS2304_TRNG_POKER_3_0       0x0060
/* "Poker"测试计数器4-7寄存器 */
#define HS2304_TRNG_POKER_7_4       0x0064
/* "Poker"测试计数器8-B寄存器 */
#define HS2304_TRNG_POKER_B_8       0x0068
/* "Poker"测试计数器C-F寄存器 */
#define HS2304_TRNG_POKER_F_C       0x006C
/* 测试控制寄存器 */
#define HS2304_TRNG_TEST            0x0070
/* 输出block计数器 */
#define HS2304_TRNG_BLOCKCNT        0x0074
/* 引擎可选信息 */
#define HS2304_TRNG_OPTIONS         0x0078
/* EIP 数字和core版本信息 */
#define HS2304_TRNG_EIP_REV         0x007C
/* TRNG全局控制寄存器0 */
#define HS2304_TRNG_CTRL0           0x00C0
/* TRNG全局控制寄存器1 */
#define HS2304_TRNG_CTRL1           0x00C4 
/* RNG控制寄存器 */
#define HS2304_RNG_CTRL             0x00CC
/* RNG随机种子配置寄存器 */
#define HS2304_RNG_SEED             0x00D0
/* TRNG core内部状态机实时状态 */
#define HS2304_TRNG_FSM_ST          0x00D4
/* RNG产生的32bit随机数 */
#define HS2304_RNG_NUM              0x00D8
/* RNG的物理环产生的随机种子 */
#define HS2304_RNG_PHY_SEED         0x00DC
/* RNG错误状态 */
#define HS2304_RNG_ERR              0x00E0
/* 中断置位寄存器 */
#define HS2304_TRNG_INT_SET         0x00E4
/* TRNG真随机数生成寄存器 */
#define HS2304_TRNG_RAN_DATA0       0x00F0
/* TRNG真随机数生成寄存器 */
#define HS2304_TRNG_RAN_DATA1       0x00F4
/* TRNG真随机数生成寄存器 */
#define HS2304_TRNG_RAN_DATA2       0x00F8
/* TRNG真随机数生成寄存器 */
#define HS2304_TRNG_RAN_DATA3       0x00FC

typedef struct {
    uint32_t trng_output_0;
    uint32_t trng_output_1;
    uint32_t trng_output_2;
    uint32_t trng_output_3;
    uint32_t trng_input_0;
    uint32_t trng_input_1;
    uint32_t trng_input_2;
    uint32_t trng_input_3;
    uint32_t trng_status;
    uint32_t trng_intack;
    uint32_t trng_control;
    uint32_t trng_config;
    uint32_t trng_alarmcnt;
    uint32_t trng_froenable;
    uint32_t trng_frodetune;
    uint32_t trng_alarmmask;
    uint32_t trng_alarmstop;
    uint32_t trng_raw_l;
    uint32_t trng_raw_h;
    uint32_t trng_spb_tests;
    uint32_t trng_count;
    uint32_t trng_cond_0;
    uint32_t trng_cond_1;
    uint32_t trng_cond_2;
    uint32_t trng_cond_3;
    uint32_t trng_cond_4;
    uint32_t trng_cond_5;
    uint32_t trng_cond_6;
    uint32_t trng_cond_7;
    uint32_t trng_key_0;
    uint32_t trng_key_1;
    uint32_t trng_key_2;
    uint32_t trng_key_3;
    uint32_t trng_key_4;
    uint32_t trng_key_5;
    uint32_t trng_key_6;
    uint32_t trng_key_7;
    uint32_t trng_ps_ai_0;
    uint32_t trng_ps_ai_1;
    uint32_t trng_ps_ai_2;
    uint32_t trng_ps_ai_3;
    uint32_t trng_ps_ai_4;
    uint32_t trng_ps_ai_5;
    uint32_t trng_ps_ai_6;
    uint32_t trng_ps_ai_7;
    uint32_t trng_ps_ai_8;
    uint32_t trng_ps_ai_9;
    uint32_t trng_ps_ai_10;
    uint32_t trng_ps_ai_11;
    uint32_t trng_run_cnt;
    uint32_t trng_run_1;
    uint32_t trng_run_2;
    uint32_t trng_run_3;
    uint32_t trng_run_4;
    uint32_t trng_run_5;
    uint32_t trng_run_6_up;
    uint32_t trng_monobitcnt;
    uint32_t trng_poker_3_0;
    uint32_t trng_poker_7_4;
    uint32_t trng_poker_b_8;
    uint32_t trng_poker_f_c;
    uint32_t trng_test;
    uint32_t trng_blockcnt;
    uint32_t trng_options;
    uint32_t trng_eip_rev;
    uint32_t trng_ctrl0;
    uint32_t trng_ctrl1;
    uint32_t rng_ctrl;
    uint32_t rng_seed;
    uint32_t trng_fsm_st;
    uint32_t rng_num;
    uint32_t rng_phy_seed;
    uint32_t rng_err;
    uint32_t trng_int_set;
    uint32_t trng_ran_data0;
    uint32_t trng_ran_data1;
    uint32_t trng_ran_data2;
    uint32_t trng_ran_data3;
} hs2304_regs;

typedef struct {
    SysBusDevice parent_obj;
    MemoryRegion iomem;
    qemu_irq     irq;

    hs2304_regs regs;

    uint32_t reg[HS2304_REGION_SIZE / sizeof(uint32_t)];
} hs2304_state;

static const VMStateDescription vmstate_hs2304 = {
    .name = TYPE_HS2304_TRNG,
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32_ARRAY(reg, hs2304_state, HS2304_REGION_SIZE / sizeof(uint32_t)),
        VMSTATE_END_OF_LIST()
    }
};

static void hs2304_reset(DeviceState *d)
{
}

static uint64_t hs2304_read_part0(void *opaque, hwaddr offset, unsigned size)
{
    hs2304_state *s = (hs2304_state *)opaque;
    hs2304_regs *regs = &s->regs;
    uint32_t ret = 0;
    switch (offset) {
        case HS2304_TRNG_OUTPUT_0:
            if ((regs->trng_status & 0x1) == 1) {
                return regs->trng_output_0;
            }
            break;
        case HS2304_TRNG_OUTPUT_1:
            if ((regs->trng_status & 0x1) == 1) {
                return regs->trng_output_1;
            }
            break;
        case HS2304_TRNG_OUTPUT_2:
            if ((regs->trng_status & 0x1) == 1) {
                return regs->trng_output_2;
            }
            break;
        case HS2304_TRNG_OUTPUT_3:
            if ((regs->trng_status & 0x1) == 1) {
                return regs->trng_output_3;
            }
            break;
        case HS2304_TRNG_STATUS:
            return regs->trng_status;
        case HS2304_TRNG_CONTROL:
            return regs->trng_control;
        case HS2304_TRNG_CONFIG:
            return regs->trng_config;
        case HS2304_TRNG_ALARMCNT:
            break;
        case HS2304_TRNG_FROENABLE:
            break;
        case HS2304_TRNG_FRODETUNE:
            return regs->trng_frodetune;
        case HS2304_TRNG_ALARMMASK:
            return regs->trng_alarmmask;
        case HS2304_TRNG_ALARMSTOP:
            return regs->trng_alarmstop;
        default:
            return -1;
    }
    return ret;
}

static uint64_t hs2304_read_part1(void *opaque, hwaddr offset, unsigned size)
{
    hs2304_state *s = (hs2304_state *)opaque;
    hs2304_regs *regs = &s->regs;
    uint32_t ret = 0;
    switch (offset) {
        case HS2304_TRNG_RAW_L:
            break;
        case HS2304_TRNG_RAW_H:
            break;
        case HS2304_TRNG_SPB_TESTS:
            break;
        case HS2304_TRNG_COUNT:
            return regs->trng_count;
        case HS2304_TRNG_RUN_CNT:
            return regs->trng_run_cnt;
        case HS2304_TRNG_RUN_1:
            return regs->trng_run_1;
        case HS2304_TRNG_RUN_2:
            return regs->trng_run_2;
        case HS2304_TRNG_RUN_3:
            return regs->trng_run_3;
        case HS2304_TRNG_RUN_4:
            return regs->trng_run_4;
        case HS2304_TRNG_RUN_5:
            return regs->trng_run_5;
        case HS2304_TRNG_RUN_6_UP:
            return regs->trng_run_6_up;
        default:
            return -1;
    }
    return ret;
}

static uint64_t hs2304_read_part2(void *opaque, hwaddr offset, unsigned size)
{
    hs2304_state *s = (hs2304_state *)opaque;
    hs2304_regs *regs = &s->regs;
    uint32_t ret = 0;
    switch (offset) {
        case HS2304_TRNG_MONOBITCNT:
            return regs->trng_monobitcnt;
        case HS2304_TRNG_POKER_3_0:
            return regs->trng_poker_3_0;
        case HS2304_TRNG_POKER_7_4:
            return regs->trng_poker_7_4;
        case HS2304_TRNG_POKER_B_8:
            return regs->trng_poker_b_8;
        case HS2304_TRNG_POKER_F_C:
            return regs->trng_poker_f_c;
        case HS2304_TRNG_TEST:
            break;
        case HS2304_TRNG_BLOCKCNT:
            return regs->trng_blockcnt;
        case HS2304_TRNG_OPTIONS:
            break;
        case HS2304_TRNG_EIP_REV:
            break;
        default:
            return -1;
    }
    return ret;
}

static uint64_t hs2304_read_part3(void *opaque, hwaddr offset, unsigned size)
{
    hs2304_state *s = (hs2304_state *)opaque;
    hs2304_regs *regs = &s->regs;
    uint32_t ret = 0;
    switch (offset) {
        case HS2304_TRNG_CTRL0:
            return regs->trng_ctrl0;
        case HS2304_TRNG_CTRL1:
            return regs->trng_ctrl1;
        case HS2304_RNG_CTRL:
            return regs->rng_ctrl;
        case HS2304_RNG_SEED:
            break;
        case HS2304_TRNG_FSM_ST:
            return regs->trng_fsm_st;
        case HS2304_RNG_NUM:
            break;
        case HS2304_RNG_PHY_SEED:
            return regs->rng_phy_seed;
        case HS2304_RNG_ERR:
            return regs->rng_err;
        case HS2304_TRNG_INT_SET:
            break;
        case HS2304_TRNG_RAN_DATA0:
            qcrypto_random_bytes(&regs->trng_ran_data0, sizeof(uint32_t), NULL);
            return regs->trng_ran_data0;
        case HS2304_TRNG_RAN_DATA1:
            qcrypto_random_bytes(&regs->trng_ran_data1, sizeof(uint32_t), NULL);
            return regs->trng_ran_data1;
        case HS2304_TRNG_RAN_DATA2:
            qcrypto_random_bytes(&regs->trng_ran_data2, sizeof(uint32_t), NULL);
            return regs->trng_ran_data2;
        case HS2304_TRNG_RAN_DATA3:
            qcrypto_random_bytes(&regs->trng_ran_data3, sizeof(uint32_t), NULL);
            return regs->trng_ran_data3;
        default:
            return -1;
    }
    return ret;
}

static uint64_t hs2304_read(void *opaque, hwaddr offset, unsigned size)
{
    hs2304_state *s = (hs2304_state *)opaque;
    hs2304_regs *regs = &s->regs;
    uint32_t ret = 0;
    if ((offset >= HS2304_TRNG_OUTPUT_0) && (offset <= HS2304_TRNG_ALARMSTOP)) {
        ret = hs2304_read_part0(opaque, offset, size);
    } else if ((offset >= HS2304_TRNG_RAW_L) && (offset <= HS2304_TRNG_RUN_6_UP)) {
        ret = hs2304_read_part1(opaque, offset, size);
    } else if ((offset >= HS2304_TRNG_MONOBITCNT) && (offset <= HS2304_TRNG_EIP_REV)) {
        ret = hs2304_read_part2(opaque, offset, size);
    } else if ((offset >= HS2304_TRNG_CTRL0) && (offset <= HS2304_TRNG_RAN_DATA3)) {
        ret = hs2304_read_part3(opaque, offset, size);
    } else {
        ret = -1;
    }
    return ret;
}

static void hs2304_write_part0(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    hs2304_state *s = (hs2304_state *)opaque;
    hs2304_regs *regs = &s->regs;
    switch (offset) {
        case HS2304_TRNG_INPUT_0:
            break;
        case HS2304_TRNG_INPUT_1:
            break;
        case HS2304_TRNG_INPUT_2:
            break;
        case HS2304_TRNG_INPUT_3:
            break;
        case HS2304_TRNG_INTACK:
            regs->trng_intack = val;
            break;
        case HS2304_TRNG_CONTROL:
            regs->trng_control = val;
            break;
        case HS2304_TRNG_CONFIG:
            regs->trng_config = val;
            break;
        case HS2304_TRNG_ALARMCNT:
            regs->trng_alarmcnt = val;
            break;
        case HS2304_TRNG_FROENABLE:
            regs->trng_froenable = val;
            break;
        case HS2304_TRNG_FRODETUNE:
            regs->trng_frodetune = val;
            break;
        case HS2304_TRNG_ALARMMASK:
            regs->trng_alarmmask = val;
            break;
        case HS2304_TRNG_ALARMSTOP:
            regs->trng_alarmstop = val;
            break;
        case HS2304_TRNG_RAW_L:
            break;
        case HS2304_TRNG_RAW_H:
            break;
        case HS2304_TRNG_SPB_TESTS:
            break;
        case HS2304_TRNG_COUNT:
            break;
        case HS2304_TRNG_RUN_CNT:
            break;
        default:
            break;
    }
}

static void hs2304_write_part1(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    hs2304_state *s = (hs2304_state *)opaque;
    hs2304_regs *regs = &s->regs;
    switch (offset) {
        case HS2304_TRNG_RUN_1:
            break;
        case HS2304_TRNG_RUN_2:
            break;
        case HS2304_TRNG_RUN_3:
            break;
        case HS2304_TRNG_RUN_4:
            break;
        case HS2304_TRNG_RUN_5:
            break;
        case HS2304_TRNG_RUN_6_UP:
            break;
        case HS2304_TRNG_MONOBITCNT:
            break;
        case HS2304_TRNG_POKER_3_0:
            break;
        case HS2304_TRNG_POKER_7_4:
            break;
        case HS2304_TRNG_POKER_B_8:
            break;
        case HS2304_TRNG_POKER_F_C:
            break;
        case HS2304_TRNG_TEST:
            break;
        case HS2304_TRNG_BLOCKCNT:
            break;
        case HS2304_TRNG_OPTIONS:
            break;
        case HS2304_TRNG_EIP_REV:
            break;
        default:
            break;
    }
}

static void hs2304_write_part2(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    hs2304_state *s = (hs2304_state *)opaque;
    hs2304_regs *regs = &s->regs;
    switch (offset) {
        case HS2304_TRNG_CTRL0:
            regs->trng_ctrl0 = val;
            break;
        case HS2304_TRNG_CTRL1:
            break;
        case HS2304_RNG_CTRL:
            break;
        case HS2304_RNG_SEED:
            break;
        case HS2304_TRNG_FSM_ST:
            break;
        case HS2304_RNG_NUM:
            break;
        case HS2304_RNG_PHY_SEED:
            break;
        case HS2304_RNG_ERR:
            break;
        case HS2304_TRNG_INT_SET:
            break;
        case HS2304_TRNG_RAN_DATA0:
            break;
        case HS2304_TRNG_RAN_DATA1:
            break;
        case HS2304_TRNG_RAN_DATA2:
            break;
        case HS2304_TRNG_RAN_DATA3:
            break;
        default:
            break;
    }
}

static void hs2304_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    hs2304_state *s = (hs2304_state *)opaque;
    hs2304_regs *regs = &s->regs;
    if ((offset >= HS2304_TRNG_INPUT_0) && (offset <= HS2304_TRNG_RUN_CNT)) {
        hs2304_write_part0(opaque, offset, val, size);
    } else if ((offset >= HS2304_TRNG_RUN_1) && (offset <= HS2304_TRNG_EIP_REV)) {
        hs2304_write_part1(opaque, offset, val, size);
    } else if ((offset >= HS2304_TRNG_CTRL0) && (offset <= HS2304_TRNG_RAN_DATA3)) {
        hs2304_write_part2(opaque, offset, val, size);
    }
}

static const MemoryRegionOps hs2304_ops = {
    .read = hs2304_read,
    .write = hs2304_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid.min_access_size = 4,
    .valid.max_access_size = 8,
    .impl.max_access_size = 4,
};

static void hs2304_init(Object *obj)
{
    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
    hs2304_state *s = HS2304_TRNG(obj);

    memory_region_init_io(&s->iomem, obj, &hs2304_ops, s, "hs2304.trng", HS2304_REGION_SIZE);
    sysbus_init_mmio(sd, &s->iomem);
    sysbus_init_irq(sd, &s->irq);
}

static void hs2304_realize(DeviceState *d, Error **errp)
{
}

static void hs2304_finalize(Object *obj)
{
}

static Property hs2304_properties[] = {
    DEFINE_PROP_END_OF_LIST(),
};

static void hs2304_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
    dc->realize = hs2304_realize;
    dc->reset = hs2304_reset;
    dc->vmsd = &vmstate_hs2304;
    device_class_set_props(dc, hs2304_properties);
}

static const TypeInfo hs2304_info = {
    .name          = TYPE_HS2304_TRNG,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(hs2304_state),
    .instance_init = hs2304_init,
    .instance_finalize = hs2304_finalize,
    .class_init    = hs2304_class_init,
};

static void hs2304_register_types(void)
{
    type_register_static(&hs2304_info);
}

type_init(hs2304_register_types)
